package org.viva.core.util;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.log4j.Logger;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.ResourceUtils;

public class RES {

	private final Logger		logger						= Logger.getLogger(RES.class);

	private final PathMatcher	pathMatcher					= new AntPathMatcher();

	public final String			CLASSPATH_ALL_URL_PREFIX	= "classpath*:";

	public Resource getResource(String location) throws Exception {
		Resource res = new PathMatchingResourcePatternResolver().getResource(location);
		return res;
	}

	/**
	 * <pre>
	 * classpath*:
	 * file:C:/context.xml
	 * classpath:/context.xml
	 * /WEB-INF/context.xml
	 * /WEB-INF/*-context.xml
	 * com/mycompany/**applicationContext.xml
	 * file:C:/some/path/*-context.xml
	 * classpath:com/mycompany/**applicationContext.xml
	 * classpath*:META-INF/beans.xml
	 * classpath*:META-INF/*-beans.xml
	 * </pre>
	 */
	public Resource[] getResources(String paths) throws IOException {
		List<Resource> retResources = new ArrayList<Resource>();
		logger.debug("find pattern: " + paths + "----------------");
		for (String path : paths.split("[,;]")) {
			Resource[] risResources = new PathMatchingResourcePatternResolver().getResources(path);
			for (Resource ri : risResources) {
				retResources.add(ri);
				logger.debug("\t|-- found resource: " + ri);
			}
		}
		Resource[] ret = new Resource[retResources.toArray().length];
		ret = retResources.toArray(ret);
		return ret;
	}

	/**
	 * Convert the given URL as returned from the ClassLoader into a Resource
	 * object. creates a UrlResource instance.
	 */
	public Resource convertClassLoaderURL(URL url) {
		return new UrlResource(url);
	}

	/**
	 * Find all resources in jar files that match the given location pattern via
	 * the Ant-style PathMatcher.
	 * 
	 * @param rootDirResource
	 *            the root directory as Resource
	 * @param subPattern
	 *            the sub pattern to match (below the root directory)
	 * @return the Set of matching Resource instances
	 */
	public Set<Resource> findAllJarResources(Resource rootDirResource, String subPattern) throws IOException {

		URLConnection con = rootDirResource.getURL().openConnection();
		JarFile jarFile;
		String jarFileUrl;
		String rootEntryPath;
		boolean newJarFile = false;

		if (con instanceof JarURLConnection) {
			// Should usually be the case for traditional JAR files.
			JarURLConnection jarCon = (JarURLConnection) con;
			jarCon.setUseCaches(false);
			jarFile = jarCon.getJarFile();
			jarFileUrl = jarCon.getJarFileURL().toExternalForm();
			JarEntry jarEntry = jarCon.getJarEntry();
			rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");
		} else {
			// No JarURLConnection -> need to resort to URL file parsing.
			// We'll assume URLs of the format "jar:path!/entry", with the
			// protocol
			// being arbitrary as long as following the entry format.
			// We'll also handle paths with and without leading "file:" prefix.
			String urlFile = rootDirResource.getURL().getFile();
			int separatorIndex = urlFile.indexOf(ResourceUtils.JAR_URL_SEPARATOR);
			if (separatorIndex != -1) {
				jarFileUrl = urlFile.substring(0, separatorIndex);
				rootEntryPath = urlFile.substring(separatorIndex + ResourceUtils.JAR_URL_SEPARATOR.length());
				jarFile = getJarFile(jarFileUrl);
			} else {
				jarFile = new JarFile(urlFile);
				jarFileUrl = urlFile;
				rootEntryPath = "";
			}
			newJarFile = true;
		}

		try {
			if (logger.isDebugEnabled()) {
				logger.debug("Looking for matching resources in jar file [" + jarFileUrl + "]");
			}
			if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) {
				// Root entry path must end with slash to allow for proper
				// matching.
				// The Sun JRE does not return a slash here, but BEA JRockit
				// does.
				rootEntryPath = rootEntryPath + "/";
			}
			Set<Resource> result = new LinkedHashSet<Resource>(8);
			for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
				JarEntry entry = entries.nextElement();
				String entryPath = entry.getName();
				if (entryPath.startsWith(rootEntryPath)) {
					String relativePath = entryPath.substring(rootEntryPath.length());
					if (pathMatcher.match(subPattern, relativePath)) {
						result.add(rootDirResource.createRelative(relativePath));
					}
				}
			}
			return result;
		} finally {
			// Close jar file, but only if freshly obtained -
			// not from JarURLConnection, which might cache the file reference.
			if (newJarFile) {
				jarFile.close();
			}
		}
	}

	public JarFile getJarFile(String jarFileUrl) throws IOException {
		if (jarFileUrl.startsWith(ResourceUtils.FILE_URL_PREFIX)) {
			try {
				return new JarFile(ResourceUtils.toURI(jarFileUrl).getSchemeSpecificPart());
			} catch (URISyntaxException ex) {
				// Fallback for URLs that are not valid URIs (should hardly ever
				// happen).
				return new JarFile(jarFileUrl.substring(ResourceUtils.FILE_URL_PREFIX.length()));
			}
		} else {
			return new JarFile(jarFileUrl);
		}
	}

	public void saveObject(String strObjectFile, Object oSave) throws Exception {
		FileOutputStream fosObject = new FileOutputStream(strObjectFile);
		ObjectOutputStream oosObject = new ObjectOutputStream(fosObject);
		oosObject.writeObject(oSave);
		oosObject.close();
		fosObject.close();
	}

	public Object loadObject(String strObjectFile) throws Exception {
		FileInputStream fis = new FileInputStream(strObjectFile);
		ObjectInputStream ois = new ObjectInputStream(fis);
		Object obj = ois.readObject();
		ois.close();
		fis.close();
		return obj;
	}

}
